{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "# Packaging an Overlay\n",
    "\n",
    "This notebook will demonstrate how to package an Overlay. This notebook depends on the previous three notebooks:\n",
    "\n",
    "   1. **[Downloading Dependencies](1-Downloading-And-Configuring.ipynb)**\n",
    "   2. **[Creating a Bitstream](2-Creating-A-Bitstream.ipynb)**\n",
    "   3. **[Compiling the RISC-V GCC Toolchain](3-Compiling-RISC-V-GCC-Toolchain)**\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Outputs from **[Creating a Bitstream](2-Creating-A-Bitstream.ipynb)**\n",
    "\n",
    "The first two critical components of a PYNQ overlay are a `.tcl` script file and a bitfile. These files should have been created in **[Creating a Bitstream](2-Creating-A-Bitstream.ipynb)** and with the names `tutorial.tcl` and `tutorial.bit`.\n",
    "\n",
    "Verify that the following files are in the `<Path-To-RISC-V-On-PYNQ>/riscvonpynq/picorv32/tut/` folder of your PYNQ-RISC-V-Tutorial repository on your host computer. \n",
    "       \n",
    "   1. tutorial.tcl (With the picorv32 IP block and all other associated IP)\n",
    "   2. tutorial.bit (Compiled using the tutorial.tcl file)\n",
    "   \n",
    "Using [SAMBA](http://pynq.readthedocs.io/en/v2.0/getting_started.html#accessing-files-on-the-board), or SCP, copy these files from your host machine to the directory `/home/xilinx/RISC-V-On-PYNQ/tutorial/riscv/` on your PYNQ board."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Python Overlay Files\n",
    "\n",
    "In this step we will create the two Python components of an Overlay: The RISC-V Overlay class definition (`tutorial.py`) and the Python Package (`__init__.py`) file."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Creating a RISC-V Overlay class definition:\n",
    "\n",
    "In your preferred editor create a new `tutorial.py` file in `<Path-To-RISC-V-On-PYNQ>/riscvonpynq/picorv32/tut/`. You can do this using the [SAMBA](http://pynq.readthedocs.io/en/v2.0/getting_started.html#accessing-files-on-the-board), over SSH, or a [Terminal interface](http://pynq-ucsd.ucsd.edu:9090/terminals/3). \n",
    "\n",
    "Using your preferred method, insert the code in the following cell into `tutorial.py`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from pynq import Overlay, GPIO, Register\n",
    "import os\n",
    "import inspect\n",
    "\n",
    "class TutorialOverlay(Overlay):\n",
    "    \"\"\"Overlay driver for the PicoRV32 bram Overlay\n",
    "\n",
    "    Note\n",
    "    ----\n",
    "    This class definition must be co-located with the .tcl and .bit\n",
    "    file for the overlay for the search path modifications in\n",
    "    riscvonpynq.Overlay to work. __init__ in riscvonpynq.Overlay uses\n",
    "    the path of this file to search for the .bit file using the\n",
    "    inspect package.\n",
    "\n",
    "    \"\"\"\n",
    "    pass\n",
    "\n",
    "class TutorialProcessor(BramProcessor):\n",
    "    \"\"\"Hierarchy driver for the PicoRV32 BRAM Processor\n",
    "\n",
    "    Note\n",
    "    ----\n",
    "    In order to be recognized as a RISC-V Processor hierarchy, three\n",
    "    conditions must be met: First, there must be a PS-Memory-Mapped\n",
    "    Block RAM Controller where the name matches the variable\n",
    "    _bram. Second, the hierarchy name (fullpath) must equal the\n",
    "    variable _name. Finally, there must be a GPIO port with the name\n",
    "    _reset_name.\n",
    "\n",
    "    Subclasses of this module are responsible for setting _name (The\n",
    "    name of the Hierarchy), _bits (Processor bit-width), _proc\n",
    "    (Processor Type Name)\n",
    "\n",
    "    This class must be placed in a known location relative to the\n",
    "    build files for this processor. The relative path can be modified\n",
    "    in __get_path.\n",
    "\n",
    "    \"\"\"\n",
    "    _name = 'tutorialProcessor'\n",
    "    _proc = 'picorv32'\n",
    "    _bits = 32\n",
    "\n",
    "    @classmethod\n",
    "    def checkhierarchy(cls, description):\n",
    "        return super().checkhierarchy(description)\n",
    "\n",
    "    def __get_path(self):\n",
    "        \"\"\"Get the directory path of this file, or the directory path of the\n",
    "        class that inherits from this class.\n",
    "\n",
    "        \"\"\"\n",
    "        # Get file path of the current class (i.e. /opt/python3.6/<...>/stream.py)\n",
    "        file_path = os.path.abspath(inspect.getfile(inspect.getmodule(self)))\n",
    "        # Get directory path of the current class (i.e. /opt/python3.6/<...>/stream/)\n",
    "        return os.path.dirname(file_path)\n",
    "\n",
    "    def __init__(self, description, *args):\n",
    "        \"\"\"Return a new Processor object. \n",
    "\n",
    "        Parameters\n",
    "        ----------\n",
    "        description : dict\n",
    "            Dictionary describing this processor.\n",
    "\n",
    "        \"\"\"\n",
    "        build_path = os.path.join(self.__get_path(), \"build\")\n",
    "        reset_value = 0\n",
    "        super().__init__(build_path, reset_value, description, *args)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Creating an \\__init.py\\__ file\n",
    "\n",
    "The `__init__.py` file is a simple file that structures packages within the python namespace. \n",
    "\n",
    "In your preferred editor create a new `__init__.py` file in the `/home/xilinx/PYNQ-RISC-V/tutorial/riscv/notebooks` folder of the PYNQ-RISC-V repository on your PYNQ Board. Insert the code from the following cell:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from . import tutorial\n",
    "from . import build"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Save and close the file. \n",
    "\n",
    "For more information on `__init__.py` files see the [Python Documentation](https://docs.python.org/3/tutorial/modules.html#packages)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Build Files \n",
    "\n",
    "Finally, we need to create a `build` folder and populate its contents.\n",
    "\n",
    "To do this, run the following cell:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!cp /home/xilinx/PYNQ-RISC-V/riscvonpynq/picorv32/bram/build/ /home/xilinx/PYNQ-RISC-V/riscvonpynq/picorv32/tut/build"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This will create build files for your processor. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Including the Tutorial processor in RISC-V-On-PYNQ\n",
    "\n",
    "To include your processor in the pip installation modify `__init__.py` in `/home/xilinx/PYNQ-RISC-V/riscvonpynq/picorv32/bram/build` with the text shown below:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from . import axi\n",
    "from . import bram\n",
    "from . import tut"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Testing the Tutorial Overlay \n",
    "\n",
    "Now that the files have been made we need to verify that they are correct. Everything that we have created gives us a working overlay.\n",
    "\n",
    "Now, for the first time, try loading your overlay by running the following cell:\n",
    "\n",
    "<div class=\"alert alert-block alert-info\">\n",
    "**Note:** The first two lines change the search path for this tutorial for this namespace (i.e. this change is not permanent)\n",
    "</div>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "\n",
       "require(['notebook/js/codecell'], function(codecell) {\n",
       "  codecell.CodeCell.options_default.highlight_modes[\n",
       "      'magic_text/x-csrc'] = {'reg':[/^%%microblaze/]};\n",
       "  Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n",
       "      Jupyter.notebook.get_cells().map(function(cell){\n",
       "          if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n",
       "  });\n",
       "});\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "\n",
       "require(['notebook/js/codecell'], function(codecell) {\n",
       "  codecell.CodeCell.options_default.highlight_modes['magic_text/x-asmx'] = {'reg':[/^%%riscvasm/]};\n",
       "  Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n",
       "      Jupyter.notebook.get_cells().map(function(cell){\n",
       "          if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n",
       "  });\n",
       "});\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "\n",
       "require(['notebook/js/codecell'], function(codecell) {\n",
       "  codecell.CodeCell.options_default.highlight_modes['magic_text/x-csrc'] = {'reg':[/^%%riscvc/]};\n",
       "  Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n",
       "      Jupyter.notebook.get_cells().map(function(cell){\n",
       "          if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n",
       "  });\n",
       "});\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/javascript": [
       "\n",
       "require(['notebook/js/codecell'], function(codecell) {\n",
       "  codecell.CodeCell.options_default.highlight_modes['magic_text/x-c++src'] = {'reg':[/^%%riscvcpp/]};\n",
       "  Jupyter.notebook.events.one('kernel_ready.Kernel', function(){\n",
       "      Jupyter.notebook.get_cells().map(function(cell){\n",
       "          if (cell.cell_type == 'code'){ cell.auto_highlight(); } }) ;\n",
       "  });\n",
       "});\n"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Help on package tut:\n",
      "\n",
      "NAME\n",
      "    tut\n",
      "\n",
      "PACKAGE CONTENTS\n",
      "    build (package)\n",
      "    tutorial\n",
      "\n",
      "FILE\n",
      "    /home/xilinx/RISC-V-On-PYNQ/riscvonpynq/picorv32/tut/__init__.py\n",
      "\n",
      "\n",
      "Help on TutorialProcessor in module tut.tutorial object:\n",
      "\n",
      "class TutorialProcessor(riscvonpynq.Processor.BramProcessor)\n",
      " |  Hierarchy driver for the PicoRV32 BRAM Processor\n",
      " |  \n",
      " |  Note\n",
      " |  ----\n",
      " |  In order to be recognized as a RISC-V Processor hierarchy, three\n",
      " |  conditions must be met: First, there must be a PS-Memory-Mapped\n",
      " |  Block RAM Controller where the name matches the variable\n",
      " |  _bram. Second, the hierarchy name (fullpath) must equal the\n",
      " |  variable _name. Finally, there must be a GPIO port with the name\n",
      " |  _reset_name.\n",
      " |  \n",
      " |  Subclasses of this module are responsible for setting _name (The\n",
      " |  name of the Hierarchy), _bits (Processor bit-width), _proc\n",
      " |  (Processor Type Name)\n",
      " |  \n",
      " |  This class must be placed in a known location relative to the\n",
      " |  build files for this processor. The relative path can be modified\n",
      " |  in __get_path.\n",
      " |  \n",
      " |  Method resolution order:\n",
      " |      TutorialProcessor\n",
      " |      riscvonpynq.Processor.BramProcessor\n",
      " |      riscvonpynq.Processor.Processor\n",
      " |      pynq.overlay.DefaultHierarchy\n",
      " |      pynq.overlay._IPMap\n",
      " |      builtins.object\n",
      " |  \n",
      " |  Methods defined here:\n",
      " |  \n",
      " |  __init__(self, description, *args)\n",
      " |      Return a new Processor object. \n",
      " |      \n",
      " |      Parameters\n",
      " |      ----------\n",
      " |      description : dict\n",
      " |          Dictionary describing this processor.\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Class methods defined here:\n",
      " |  \n",
      " |  checkhierarchy(description) from pynq.overlay.RegisterHierarchy\n",
      " |      Function to check if the driver matches a particular hierarchy\n",
      " |      \n",
      " |      This function should be redefined in derived classes to return True\n",
      " |      if the description matches what is expected by the driver. The default\n",
      " |      implementation always returns False so that drivers that forget don't\n",
      " |      get loaded for hierarchies they don't expect.\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Methods inherited from riscvonpynq.Processor.Processor:\n",
      " |  \n",
      " |  land(self)\n",
      " |      Terminate execution on a RISC-V Processor, unload the program and\n",
      " |      arguments, and return the program's return value (if it terminated). \n",
      " |      \n",
      " |      Note\n",
      " |      ----\n",
      " |      Any modified data in the program argument buffers will be\n",
      " |      copied back to the original buffer (if applicable) by _unload()\n",
      " |  \n",
      " |  launch(self, prog, *args)\n",
      " |      Launch a given program on the RISC-V Processor, and return while\n",
      " |      the program executes.\n",
      " |      \n",
      " |      Parameters\n",
      " |      ----------\n",
      " |      prog : riscvonpynq.Program\n",
      " |          A Program class from the riscvonpynq library that wraps a\n",
      " |          user program\n",
      " |      \n",
      " |      args : tuple\n",
      " |          Arguments to pass to the main method of the program\n",
      " |  \n",
      " |  run(self, prog, *args)\n",
      " |      Run a given program on the RISC-V Processor, and block until the\n",
      " |      program terminates.\n",
      " |      \n",
      " |      Parameters\n",
      " |      ----------\n",
      " |      prog : riscvonpynq.Program\n",
      " |          A Program class from the riscvonpynq library that wraps a\n",
      " |          user program\n",
      " |      \n",
      " |      args : tuple\n",
      " |          Arguments to pass to the main method of the program\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Data descriptors inherited from riscvonpynq.Processor.Processor:\n",
      " |  \n",
      " |  bits\n",
      " |      Return the bit width of the RISC-V Processor IP\n",
      " |  \n",
      " |  build_path\n",
      " |      Return the path to the build directory for this RISC-V Processor\n",
      " |  \n",
      " |  name\n",
      " |      Return the name of the RISC-V Processor Hierarchy\n",
      " |  \n",
      " |  proc\n",
      " |      Return the name of the RISC-V Processor IP RISC-V Processor\n",
      " |      Hierarchy\n",
      " |  \n",
      " |  stkptr\n",
      " |      Return the memory size in Bytes of the desired stack pointer\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Methods inherited from pynq.overlay._IPMap:\n",
      " |  \n",
      " |  __dir__(self)\n",
      " |      __dir__() -> list\n",
      " |      default dir() implementation\n",
      " |  \n",
      " |  __getattr__(self, key)\n",
      " |  \n",
      " |  ----------------------------------------------------------------------\n",
      " |  Data descriptors inherited from pynq.overlay._IPMap:\n",
      " |  \n",
      " |  __dict__\n",
      " |      dictionary for instance variables (if defined)\n",
      " |  \n",
      " |  __weakref__\n",
      " |      list of weak references to the object (if defined)\n",
      "\n"
     ]
    }
   ],
   "source": [
    "import sys\n",
    "sys.path.insert(0, '/home/xilinx/RISC-V-On-PYNQ/riscvonpynq/picorv32/')\n",
    "\n",
    "from tut.tutorial import TutorialOverlay\n",
    "\n",
    "overlay = TutorialOverlay(\"tutorial.bit\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If the cell above fails, check that you have completed the steps above correctly.\n",
    "\n",
    "To test code on your processor, run the following Jupyter magic. This cell defines a main method. It expects two arguments. The first argument is the path of the file on the ARM PS. The second argument is an array of at least three elements. \n",
    "\n",
    "The program returns the third element of the second argument.\n",
    "\n",
    "Run the following cell to compile it. If the cell fails, check that `<Path-To-RISC-V-On-PYNQ>/riscvonpynq/picorv32/tut/build` exists."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<pre>Compilation of program test SUCCEEDED</pre>"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "%%riscvc test overlay.tutorialProcessor\n",
    "\n",
    "int main(int argc, char ** argv){\n",
    "    unsigned int * arr = (unsigned int *)argv[1];\n",
    "    return arr[2];\n",
    "}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To run the program above, execute the following cell. The first argument will automatically be created by the `run` method of the tutorialProcessor"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Test passed!\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "arg1 = np.array([4,2,3], np.uint32)\n",
    "\n",
    "retval = overlay.tutorialProcessor.run(test, arg1)\n",
    "\n",
    "if(retval != arg1[2]):\n",
    "    print(\"Test failed!\")\n",
    "else:\n",
    "    print(\"Test passed!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If the test passed, congratulations!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Installing the Tutorial Overlay\n",
    "\n",
    "By modifiying the `__init__.py` files in `<Path-To-RISC-V-On-PYNQ>/riscvonpynq/picorv32/` and `<Path-To-RISC-V-On-PYNQ>/riscvonpynq/picorv32/tut` we have added the overlay created in this tutorial to the `riscvonpynq` package in the local repository. To install it formally, run the following cell: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip3.6 install --upgrade /home/xilinx/RISC-V-On-PYNQ/"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "If the cell above completes successfully, you can now run the previous cells without modifying the system path:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from riscvonpynq.picorv32.tut.tutorial import TutorialOverlay\n",
    "\n",
    "overlay = TutorialOverlay(\"tutorial.bit\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "%%riscvc test overlay.tutorialProcessor\n",
    "\n",
    "int main(int argc, char ** argv){\n",
    "    unsigned int * arr = (unsigned int *)argv[1];\n",
    "    return arr[2];\n",
    "}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "arg1 = np.array([4,2,3], np.uint32)\n",
    "\n",
    "retval = overlay.tutorialProcessor.run(test, arg1)\n",
    "\n",
    "if(retval != arg1[2]):\n",
    "    print(\"Test failed!\")\n",
    "else:\n",
    "    print(\"Test passed!\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.0"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
